﻿/*
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
*/
package com.jorritdevries.console
{
	import com.jorritdevries.console.utils.parseBoolean;

	/**
	 * The CommandParser is a helper class for the Console. It reads the given
	 * command line and acts upon.
	 * @author jorrit@jorritdevries.com
	 */
	internal class CommandParser
	{
		// Reference to the Console this CommandParser is serving.
		private var mConsole:Console;		
		
		/**
		 * 
		 * @param console
		 */
		public function CommandParser(console:Console)
		{
			mConsole = console;
		}
		
		/**
		 * Parses the given String. If succesfully parsed, the appropiate operation
		 * will be executed. If unsuccesfull, the default command of the Console
		 * will be called.
		 * @param commandLine  A space-seperated String specifying the command
		 * and the arguments to be parsed.
		 */
		public function parseCommandLine(commandLine:String):void
		{
			// Tokenize the command line
			var args:Array = commandLine.split(" ");
			// If there are no arguments
			if (!args.length)
			{
				return;
			}
			// Execute the command
			for (var i:String in mConsole.mItemList)
			{
				var item:ConsoleItem = ConsoleItem(mConsole.mItemList[i]);
				if (item.name == args[0])
				{
					// Can only have on argument on data members
					if (item.type != ConsoleItemType.FUNCTION && args.length > 2)
					{
						mConsole.print("The command '" + commandLine + "' contains too many parameters.");
						mConsole.mDefaultCommand(args);
						return;
					}
					execute(args, item);
					return;
				}
			}
			// Since no item was found, call default
			args.shift();
			mConsole.mDefaultCommand(args);
		}
		
		/**
		 * Executes the commands for the ConsoleItem based on the number of
		 * arguments passed.
		 * @param args An Array of Strings
		 * @param item ConsoleItem to execute
		 */
		private function execute(args:Array, item:ConsoleItem):void
		{
			switch (item.type)
			{
			case ConsoleItemType.UINT:
				if (!(item.any[item.varName] is uint))
				{
					throwParseException(item.name, "uint");
				}
				// Read data
				if (args.length == 1)
				{
					var ui:uint = uint(item.any[item.varName]);
					mConsole.print(item.name + " : " + ui.toString());
					return;
				}
				// Write data
				else
				{
					item.any[item.varName] = uint(parseInt(args[1]));
					return;
				}
			case ConsoleItemType.INT:
				if (!(item.any[item.varName] is int))
				{
					throwParseException(item.name, "int");
				}
				// Read data
				if (args.length == 1)
				{
					var i:int = int(item.any[item.varName]);
					mConsole.print(item.name + " : " + i.toString());
					return;
				}
				// Write data
				else
				{
					item.any[item.varName] = int(parseInt(args[1]));
					return;
				}
			case ConsoleItemType.NUMBER:
				if (!(item.any[item.varName] is Number))
				{
					throwParseException(item.name, "Number");
				}
				// Read data
				if (args.length == 1)
				{
					var n:Number = Number(item.any[item.varName]);
					mConsole.print(item.name + " : " + n.toString());
					return;
				}
				// Write data
				else
				{
					item.any[item.varName] = parseFloat(args[1]);
					return;
				}
			case ConsoleItemType.BOOLEAN:
				if (!(item.any[item.varName] is Boolean))
				{
					throwParseException(item.name, "Boolean");
				}
				// Read data
				if (args.length == 1)
				{
					var b:Boolean = Boolean(item.any[item.varName]);
					mConsole.print(item.name + " : " + b.toString());
					return;
				}
				// Write data
				else
				{
					item.any[item.varName] = parseBoolean(args[1]);
					return;
				}
			case ConsoleItemType.STRING:
				if (!(item.any[item.varName] is String))
				{
					throwParseException(item.name, "String");
				}
				// Read data
				if (args.length == 1)
				{
					var str:String = String(item.any[item.varName]);
					mConsole.print(item.name + " : " + str);
					return;
				}
				// Write data
				else
				{
					item.any[item.varName] = String(args[1]);
					return;
				}
			case ConsoleItemType.FUNCTION:
				if (!(item.any is Function))
				{
					throwParseException(item.name, "Function");
				}
				else
				{
					// Cannot cast to Function
					var func:Function = item.any;
					// Remove first element from list (this is the item name)
					args.shift();
					func(args);
					return;
				}
			default:
				args.shift();
				mConsole.mDefaultCommand(args);
			}
		}
		
		/** Throws an exception specifying the parse error that occurred. */
		private function throwParseException(itemName:String, itemType:String):void
		{
			throw new Error("CommandParseException: An invalid reference to a '" + itemType + 
				"' is passed to DebugConsole.addItem for '" + itemName + "'.");
		}
	}
	
}